export function $(expr, con) {
	return typeof expr === 'string' ? (con || document).querySelector(expr) : expr || null;
}

export function createSVG(tag, attrs) {
	const elem = document.createElementNS('http://www.w3.org/2000/svg', tag);
	for (let attr in attrs) {
		if (attr === 'append_to') {
			const parent = attrs.append_to;
			parent.appendChild(elem);
		} else if (attr === 'innerHTML') {
			elem.innerHTML = attrs.innerHTML;
		} else {
			elem.setAttribute(attr, attrs[attr]);
		}
	}
	return elem;
}

export function animateSVG(svgElement, attr, from, to) {
	const animatedSvgElement = getAnimationElement(svgElement, attr, from, to);

	if (animatedSvgElement === svgElement) {
		// triggered 2nd time programmatically
		// trigger artificial click event
		const event = document.createEvent('HTMLEvents');
		event.initEvent('click', true, true);
		event.eventName = 'click';
		animatedSvgElement.dispatchEvent(event);
	}
}

function getAnimationElement(svgElement, attr, from, to, dur = '0.4s', begin = '0.1s') {
	const animEl = svgElement.querySelector('animate');
	if (animEl) {
		$.attr(animEl, {
			attributeName: attr,
			from,
			to,
			dur,
			begin: 'click + ' + begin // artificial click
		});
		return svgElement;
	}

	const animateElement = createSVG('animate', {
		attributeName: attr,
		from,
		to,
		dur,
		begin,
		calcMode: 'spline',
		values: from + ';' + to,
		keyTimes: '0; 1',
		keySplines: cubic_bezier('ease-out')
	});
	svgElement.appendChild(animateElement);

	return svgElement;
}

function cubic_bezier(name) {
	return {
		ease: '.25 .1 .25 1',
		linear: '0 0 1 1',
		'ease-in': '.42 0 1 1',
		'ease-out': '0 0 .58 1',
		'ease-in-out': '.42 0 .58 1'
	}[name];
}

$.on = (element, event, selector, callback) => {
	if (!callback) {
		callback = selector;
		$.bind(element, event, callback);
	} else {
		$.delegate(element, event, selector, callback);
	}
};

$.off = (element, event, handler) => {
	element.removeEventListener(event, handler);
};

$.bind = (element, event, callback) => {
	event.split(/\s+/).forEach(function (event) {
		element.addEventListener(event, callback);
	});
};

$.delegate = (element, event, selector, callback) => {
	element.addEventListener(event, function (e) {
		const delegatedTarget = e.target.closest(selector);
		if (delegatedTarget) {
			e.delegatedTarget = delegatedTarget;
			callback.call(this, e, delegatedTarget);
		}
	});
};

$.closest = (selector, element) => {
	if (!element) return null;

	if (element.matches(selector)) {
		return element;
	}

	return $.closest(selector, element.parentNode);
};

$.attr = (element, attr, value) => {
	if (!value && typeof attr === 'string') {
		return element.getAttribute(attr);
	}

	if (typeof attr === 'object') {
		for (let key in attr) {
			$.attr(element, key, attr[key]);
		}
		return;
	}

	element.setAttribute(attr, value);
};
